package com.supermap.services.security.ext.gzca;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.web.env.IniWebEnvironment;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.slf4j.cal10n.LocLogger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.Ordered;

import com.supermap.services.util.LogUtil;
import com.supermap.services.util.ResourceManager;


public class ContainerInitializer implements ServletContainerInitializer, ServletContextListener {
    private static LocLogger logger = LogUtil.getLocLogger(ContainerInitializer.class, ResourceManager.getCommontypesResource());
    private FileSystemXmlApplicationContext applicationContext;
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        if(applicationContext != null) {
            applicationContext.close();
        }
    }
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        try {
            doStartup(c, ctx);
            ctx.addListener(this);
        } catch (IOException e) {
            throw new ServletException(e);
        } catch(AHa e) {
            logger.error(e.getMessage());
            e.context.close();
            throw new ServletException(e.getMessage());
        }
    }

    private void doStartup(Set<Class<?>> c, ServletContext ctx) throws IOException {
        final File webappHome = new File(ctx.getRealPath("./")).getCanonicalFile();
        File defaultConfigDir = new File(webappHome, "WEB-INF");
        File beanFile = new File(defaultConfigDir, "GZCAbeans.xml");
        if(!beanFile.exists()) {
            logger.info("{} does not exists.", beanFile);
            return;
        }
        applicationContext = loadSpringBean(Collections.emptySet(), new Properties(), beanFile);
        Arrays.asList("authorizeEndpoint","platCode","secretKey","tokenEndpoint","userInfoEndpoint").forEach(name -> {
            String value = applicationContext.getBean(name, String.class);
            if(StringUtils.isEmpty(value)) {
                throw new AHa(applicationContext, name + " is empty.");
            }
        });
        EnumSet<DispatcherType> noneDispatcherSet = EnumSet.noneOf(DispatcherType.class);
        ctx.addFilter("configFilter", applicationContext.getBean("configFilter", Filter.class)).addMappingForUrlPatterns(noneDispatcherSet, false, "/resources/web-ui/extend/login/config.json");
        ctx.addListener(new ServletContextAttributeListener() {
            
            @Override
            public void attributeReplaced(ServletContextAttributeEvent event) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void attributeRemoved(ServletContextAttributeEvent event) {
                // TODO Auto-generated method stub
                
            }
            
            @Override
            public void attributeAdded(ServletContextAttributeEvent event) {
                Object value = event.getValue();
                if(value instanceof IniWebEnvironment) {
                    IniWebEnvironment env = (IniWebEnvironment) value;
                    if(env.getFilterChainResolver() instanceof PathMatchingFilterChainResolver) {
                        FilterChainManager chainManager = ((PathMatchingFilterChainResolver) env.getFilterChainResolver()).getFilterChainManager();
                        String gzcaFilterName = "gzcaFilter";
                        Filter gzcaFilter = applicationContext.getBean(gzcaFilterName, Filter.class);
                        chainManager.addFilter(gzcaFilterName, gzcaFilter);
                        chainManager.createChain("/gzca/*", gzcaFilterName);
                    } else {
                        throw new IllegalStateException();
                    }
                }
            }
        });
        
    }

    private FileSystemXmlApplicationContext loadSpringBean(final Set<Entry<String, Object>> ctx, Properties properties, File xmlLocation) throws IOException {
        FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext();
        context.setConfigLocations("file:" + xmlLocation.getCanonicalPath());
        PropertyPlaceholderConfigurer propertiesProcessor = new PropertyPlaceholderConfigurer();
        propertiesProcessor.setProperties(properties);
        propertiesProcessor.setIgnoreUnresolvablePlaceholders(true);
        propertiesProcessor.setOrder(Ordered.HIGHEST_PRECEDENCE);
        context.addBeanFactoryPostProcessor(propertiesProcessor);
        context.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {
            @Override
            public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
                for (Entry<String, Object> entry : ctx) {
                    beanFactory.registerSingleton(entry.getKey(), entry.getValue());
                }
            }
        });
        context.refresh();
        return context;
    }
    
    private static class AHa extends RuntimeException {
        private static final long serialVersionUID = 5199052533004600895L;
        private FileSystemXmlApplicationContext context;

        AHa(FileSystemXmlApplicationContext context, String msg) {
            super(msg);
            this.context = context;
        }
    }

}
